<link href='web-animations.css' rel='stylesheet' type='text/css'> 
<pre class='metadata'>
Title: Scroll-linked Animations
Group: CSSWG
Status: UD
Work Status: exploring
Group: CSSWG
URL: https://drafts.csswg.org/scroll-animations-1/
ED: https://drafts.csswg.org/scroll-animations-1/
Shortname: scroll-animations
Abstract: Defines an API and markup for creating animations that are either
          triggered by or tied to the scroll offset of a scroll container.
Editor: Brian Birtles <bbirtles@mozilla.com>
Editor: Botond Ballo <botond@mozilla.com>
Editor: Stephen McGruer <smcgruer@google.com>
Editor: Antoine Quint <graouts@apple.com>
Former editor: Mantaroh Yoshinaga
</pre>
<pre class=anchors>
urlPrefix: https://w3c.github.io/web-animations/; type: dfn; spec: web-animations
    text: animation; url: concept-animation
    text: current time
    text: default document timeline
    text: duration
    text: inactive timeline
    text: start delay
    text: target effect end
    text: timeline
urlPrefix: https://drafts.csswg.org/cssom-view/; type: dfn; spec: cssom-view-1
    text: CSS layout box
urlPrefix: https://html.spec.whatwg.org/multipage/browsers.html; type: dfn; spec: html
    text: document associated with a window; url: concept-document-window
</pre>
<pre class=link-defaults>
spec:html; type:dfn; for:/; text:browsing context
</pre>

# Introduction # {#intro}

This specification defines mechanisms for 
[[#scroll-driven-animations|driving the progress of an animation]] based
on the scroll progress of a scroll container.

## Relationship to other specifications ## {#other-specs}

Web Animations [[WEB-ANIMATIONS-1]] defines an abstract conceptual model for
animations on the Web platform, with elements of the model including
[=animations=] and their [=timelines=],
and associated programming interfaces.

This specification extends this model by defining a new type of animation [=timeline=]:
a [=scroll timeline=].

This specification defines both programming interfaces for interacting with these
concepts, as well as CSS markup which applies these concepts to CSS Animations
[[CSS3-ANIMATIONS]].

The behavior of the CSS markup is described in terms of the programming interfaces.
User agents that do not support script may still implement the CSS markup
provided it behaves as if the underlying programming interfaces were in place.

## Relationship to asynchronous scrolling ## {#async-scrolling}

Some user agents support scrolling that's asynchronous with respect to layout
or script. This specification is intended to be compatible with such an
architecture.

Specifically, this specification allows expressing scroll-linked effects in a
way that does not require script to run each time the effect is sampled. User
agents that support asynchronous scrolling are allowed (but not required) to
sample such effects asynchronously as well.

# Use cases # {#use-cases}

<em>This section is non-normative</em>

Note: Based on this <a
href="https://github.com/WICG/scroll-animations/blob/master/Use%20cases.md">curated
list of use cases</a>.

## Scroll-triggered animations ## {#scroll-triggered-animations-usecases}

### Navigation bar shrinking effect ### {#navigation-bar-shrinking-effect}

It is common to trigger an animation to run when the scroll position
reaches a certain point. For example, a navigation bar may shrink once
the user begins to scroll a page.

<figure>
<img src="img/usecase1-2.svg" width="600"
alt="Use case: Shrinking navigation bar">
 <figcaption>
  Shrinking navigation bar<br>
  The left figure shows the navigation bar before scrolling with
  a large menu bar.<br>
  The right figure shows the shrunken navigation bar after scrolling.
 </figcaption>
</figure>

Advisement: The proposal does not yet define CSS markup or programming
            interfaces to express this use case.

### Navigation highlight effect ### {#navigation-highlight-effect}

Similarly, it is common to trigger an animation at certain fixed points
in a element's scroll range. For example, a navigation bar that changes
highlight based on the reader's position within the document.

<figure>
<img src="img/usecase1-1.svg" width="600"
alt="Use case: A navigation highlight effect.">
 <figcaption>
  A navigation highlight effect<br>
  On the left, the &ldquo;Abstract&rdquo; section is scrolled into view
  and hence the abstract menu item is highlighted.<br>
  After scrolling down to the &ldquo;Background&rdquo; section (right),
  the background menu item fades in while the abstract menu item fades
  out.
 </figcaption>
</figure>

Advisement: The proposal does not yet define CSS markup or programming
            interfaces to express this use case.

## Scroll-triggered style changes ## {#scroll-triggered-style-changes-usecase}

Advisement: The proposal does not yet define CSS markup or programming
            interfaces to express this use case.


## Scroll-linked animations ## {#scroll-linked-animations-usecase}

### Scrollable picture-story show ### {#scrollable-animation-usecase}

Another pattern is an animation that tells a story where the user
controls the progress of the animation by scrolling or some other
gesture. This may be because the animation contains a lot of textual
information which the user may wish to peruse more slowly, it may be for
accessibility considerations to accommodate users who are uncomfortable
with rapid animation, or it may simply be to allow the user to easily
return to previous parts of the story such as a story that introduces
a product where the user wishes to review previous information.

The following (simplified) example shows two balls colliding. The
animation is controlled by scroll position allowing the user to easily
rewind and replay the interaction.

<figure>
<img src="img/usecase3-1.svg" width="600"
alt="Use case: The picture-story show.">
 <figcaption>
  A scrollable movie.<br>
  The left figure shows the initial position of the balls<br>
  The right figure shows them after they have collided.
 </figcaption>
</figure>

Using the CSS markup:

<pre class='lang-css'>
@media (prefers-reduced-motion: no-preference) {
  div.circle {
    animation-duration: 1s;
    animation-timing-function: linear;
    animation-timeline: scroll(element(#container), vertical, "200px", "300px");
  }
  #left-circle {
    animation-name: left-circle;
  }
  #right-circle {
    animation-name: right-circle;
  }
  #union-circle {
    animation-name: union-circle;
    animation-timeline: scroll(element(#container), vertical, "250px", "300px");
  }
  @keyframes left-circle {
    to { transform: translate(300px) }
  }
  @keyframes right-circle {
    to { transform: translate(350px) }
  }
  @keyframes union-circle {
    to { opacity: 1 }
  }
}
</pre>

Using the programming interface, we might write this as:

<pre class='lang-javascript'>
if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
  var circleTimeline = new ScrollTimeline({
    scrollSource: scrollableElement,
    scrollOffset: '200px',
    endScrollOffset: '300px'
  });

  var left = leftCircle.animate({ transform: 'translate(300px)' }, 1000);
  left.timeline = circleTimeline;

  var right = leftCircle.animate({ transform: 'translate(350px)' }, 1000);
  right.timeline = circleTimeline;

  var union = unionCircle.animate({ opacity: 1 }, 1000);
  union.timeline = new ScrollTimeline({
    scrollSource: scrollableElement,
    startScrollOffset: '250px',
    endScrollOffset: '300px'
  });
}
</pre>

### The content progress bar ### {#content-progress-bar-usecase}

Another common example of an animation that tracks scroll position is a
progress bar that is used to indicate the reader's position in a long
article.

<figure>
<img src="img/usecase3-2.svg" width="600"
alt="Use case: Scroll based styling">
 <figcaption>
  Content progress bar.<br>
  The left figure shows the initial state before scrolling.<br>
  The right figure shows the progress bar is half-filled in since the
  user has scrolled half way through the article.
 </figcaption>
</figure>

Typically, the scroll bar provides this visual indication but
applications may wish to hide the scroll bar for aesthetic or useability
reasons.

Using the 'animation-timeline' property, this example could be written
as follows:

<pre class='lang-css'>
@media (prefers-reduced-motion: no-preference) {
  @keyframes progress {
    to { width: 100%; }
  }
  #progress {
    width: 0px;
    height: 30px;
    background: red;
    animation: progress 1s linear;
    animation-timeline: scroll(element(#body));
  }
}
</pre>

If we use this API for this case, the example code will be as follow:

<pre class='lang-javascript'>
if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
  var animation = div.animate({ width: '100%' }, 1000);
  animation.timeline = new ScrollTimeline(
    { startScrollOffset: '0px' }
  );
}
</pre>

## Combination scroll and time-base animations ## {#combination-scroll-and-time-base-animations-usecase}

### Photo viewer ### {#movie-show-case-usecase}

Advisement: We are currently reworking this use case

<!--
Maybe the developer will want to use the scroll based timeline and the time-based timeline.

Here's an example content which showing the photos.
If scroll position is out of specified range, the animation of the slideshow will start. The progress of this slideshow is related to scroll volume. And if scroll position is within the specified range, the animation of the slideshow will continue automatically.

<figure>
<img src="img/usecase4.svg" width="600"
alt="Use case 4: Scrollable slide show.">
 <figcaption>
  Use case 4: Scrollable slide show.<br>
  The left figure is before scroll, the slide show will start as scroll-linked animation.<br>
  The right figure is after scroll, the slide show will start as related to the time animation.
 </figcaption>
</figure>

This content can't build the CSS only.
<pre class='lang-javascript'>
if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
  var animation = slideTarget.getAnimation()[0];
  var scrollTimeline = new ScrollTimeline({
    scrollSource: scrollableElement,
    orientation: "vertical",
    scrollOffset: '0px',
    endScrollOffset: '200px'
  });
  animation.timeline = scrollTimeline;

  // We use scroll event in order to change the timeline.
  scrollableElement.addEventListener("scroll", function(evt) {
    if ((scrollableElement.scrollTop > 200) && animation.timeline != document.timeline) {
      animation.timeline = document.timeline;
    } else if ((scrollableElement.scrollTop < 200) && animation.timeline == document.timeline) {
      animation.timeline = scrollTimeline;
    }
  });
}
</pre>
-->

</div>

# Scroll-driven animations # {#scroll-driven-animations}

## Scroll timelines ## {#scroll-timelines}

### The {{ScrollDirection}} enumeration ### {#scrolldirection-enumeration}

<pre class="idl">
enum ScrollDirection {
  "block",
  "inline",
  "horizontal",
  "vertical"
};
</pre>

The {{ScrollDirection}} enumeration specifies a direction of scroll of a
scrollable element.

:   <code>block</code>
::  Selects the direction along the [=block axis=], conforming to writing mode
    and directionality.

:   <code>inline</code>
::  Selects the direction along the [=inline axis=], confirming to writing mode
    and directionality.

:   <code>horizontal</code>
::  Selects the physical horizontal direction (ignoring writing mode and
    directionality).

:   <code>vertical</code>
::  Selects the physical vertical direction (ignoring writing mode and
    directionality).

Note: Having both logical (block/inline) and physical (vertical/horizontal)
directions allows web developers to animate both logical (e.g.
margin-inline-start) and physical (e.g. transform) properties with good
behavior under different directionalities and writing modes.

### The {{ScrollTimeline}} interface ### {#scrolltimeline-interface}

<pre class="idl">
enum ScrollTimelineAutoKeyword { "auto" };

dictionary ScrollTimelineOptions {
  Element? scrollSource = null;
  ScrollDirection orientation = "block";
  DOMString startScrollOffset = "auto";
  DOMString endScrollOffset = "auto";
  (double or ScrollTimelineAutoKeyword) timeRange = "auto";
  FillMode fill = "none";
};

[Exposed=Window,
 Constructor(optional ScrollTimelineOptions options = {})]
interface ScrollTimeline : AnimationTimeline {
  readonly attribute Element scrollSource;
  readonly attribute ScrollDirection orientation;
  readonly attribute DOMString startScrollOffset;
  readonly attribute DOMString endScrollOffset;
  readonly attribute (double or ScrollTimelineAutoKeyword) timeRange;
  readonly attribute FillMode fill;
};
</pre>

A <dfn>scroll timeline</dfn> is an {{AnimationTimeline}} whose time values are determined
not by wall-clock time, but by the progress of scrolling in a [=scroll container=].

<div link-for-hint="ScrollTimeline">

<div class="constructors">

:   <dfn constructor for=ScrollTimeline lt="ScrollTimeline(options)">ScrollTimeline(options)</dfn>
::  Creates a new {{ScrollTimeline}} object using the following procedure:

    1. Let |timeline| be a new {{ScrollTimeline}} object.

    1. Let |source| be the result corresponding to the first matching condition from below.

        : If the |scrollSource| value of |options| is non-null,
        :: Let |source| be |scrollSource|

        : Otherwise (|scrollSource| is null):
        :: Let |source| be the {{scrollingElement}} of the {{Document}} <a lt="document associated with a window">associated</a> with the {{Window}} that is the <a>current global object</a>.

        Note: |source| may still be null after this step, e.g. if the {{Document}} has no {{scrollingElement}}.

    1. Set the {{ScrollTimeline/scrollSource}} of |timeline| to |source|.

    1. Assign the {{ScrollTimeline/orientation}}, {{ScrollTimeline/startScrollOffset}}, {{ScrollTimeline/endScrollOffset}}, {{ScrollTimeline/timeRange}}, and {{ScrollTimeline/fill}} properties of |timeline| to the corresponding value from |options|.

</div>

<div class="attributes">

:   <dfn attribute for=ScrollTimeline>scrollSource</dfn>
::  The scrollable element whose scrolling triggers the activation and drives the
    progress of the timeline.

:   <dfn attribute for=ScrollTimeline>orientation</dfn>
::  Determines the direction of scrolling which triggers the activation and drives
    the progress of the trigger.

:   <dfn attribute for=ScrollTimeline>startScrollOffset</dfn>
::  A scroll offset, in the direction specified by {{orientation}}, which constitutes
    the beginning of the range in which the timeline is active.

    Recognized values are defined by the following grammar:

    <blockquote>
      <pre class="prod">auto | <<length>> | <<percentage>></pre>
    </blockquote>

    The meaning of each value is as follows:

    :   auto
    ::  The beginning of {{scrollSource}}'s scroll range in {{orientation}}.
    :   <<length>>
    ::  An absolute distance along {{scrollSource}}'s scroll range in {{orientation}}.
    :   <<percentage>>
    ::  A percentage distance along {{scrollSource}}'s scroll range in {{orientation}}.

:   <dfn attribute for=ScrollTimeline>endScrollOffset</dfn>
::  A scroll offset, in the direction specified by {{orientation}}, which constitutes 
    the end of the range in which the trigger is activated.

    Recognized values are defined by the following grammar:

    <blockquote>
      <pre class="prod">auto | <<length>> | <<percentage>></pre>
    </blockquote>

    The meaning of each value is as follows:

    :   auto
    ::  The end of {{scrollSource}}'s scroll range in {{orientation}}.
    :   <<length>>
    ::  An absolute distance along {{scrollSource}}'s scroll range in {{orientation}}.
    :   <<percentage>>
    ::  A percentage distance along {{scrollSource}}'s scroll range in {{orientation}}.

:   <dfn attribute for=ScrollTimeline>timeRange</dfn>
::  A time duration that allows mapping between a distance scrolled, and
    quantities specified in time units, such as an animation's [=duration=] and
    [=start delay=].

    Conceptually, {{timeRange}} represents the number of milliseconds to map to the 
    scroll range defined by {{startScrollOffset}} and {{endScrollOffset}}. As a 
    result, this value does not have a correspondence to wall-clock time.

    This value is used to compute the timeline's [=effective time range=], and
    the mapping is then defined by mapping the scroll distance from 
    {{startScrollOffset}} to {{endScrollOffset}}, to the [=effective time range=].

:   <dfn attribute for=ScrollTimeline>fill</dfn>
::  Determines whether the timeline is active even when the scroll offset is outside
    the range defined by [{{startScrollOffset}}, {{endScrollOffset}}].

    Possible values are:

    :   none
    ::  The timeline is inactive when the scroll offset is less than {{startScrollOffset}} 
        or greater than or equal to {{endScrollOffset}}.

    :   forwards
    ::  When the scroll offset is less than {{startScrollOffset}}, the
        timeline is inactive.
        When the scroll offset is greater than or equal to the
        {{endScrollOffset}}, the timeline's [=current time=] is its
        [=effective time range=].

    :   backwards
    ::  When the scroll offset is less than {{startScrollOffset}}, the
        timeline's [=current time=] is 0.
        When the scroll offset is greater than or equal to the
        {{endScrollOffset}}, the timeline is inactive.

    :   both
    ::  When the scroll offset is less than {{startScrollOffset}}, the
        timeline's [=current time=] is 0.
        When the scroll offset is greater than or equal to the
        {{endScrollOffset}}, the timeline's [=current time=] is its
        [=effective time range=].

    :   auto
    ::  Behaves the same as <code>both</code>.

</div>

### The effective time range of a {{ScrollTimeline}} ### {#efffective-time-range-algorithm}

The <dfn>effective time range</dfn> of a {{ScrollTimeline}} is calculated as follows:

<div class="switch">

:   If the {{timeRange}} has the value <code>"auto"</code>,
::  The [=effective time range=] is the maximum value of the 
    [=target effect end=] of all animations
    directly associated with this timeline.

    If any animation directly associated with the timeline has a 
    [=target effect end=] of infinity, the [=effective time range=] 
    is zero.

:   Otherwise,
::  The [=effective time range=] is the {{ScrollTimeline}}'s 
    {{timeRange}}.

</div>

### The current time of a {{ScrollTimeline}} ### {#current-time-algorithm}

The [=current time=] of a {{ScrollTimeline}} is calculated
as follows:

1.  If {{scrollSource}} is null, does not currently have a [=CSS layout box=], or
    if its layout box is not a [=scroll container=], return an unresolved time value.

1.  Otherwise, let <var>current scroll offset</var> be the current scroll offset of {{scrollSource}}
    in the direction specified by {{orientation}}.

1.  If <var>current scroll offset</var> is less than {{startScrollOffset}}, return an unresolved
    time value if {{fill}} is <code>none</code> or <code>forwards</code>, or 0 otherwise.

1.  If <var>current scroll offset</var> is greater than or equal to {{endScrollOffset}} then:
    <div class="switch">

    : If {{endScrollOffset}} is less than the maximum scroll offset of {{scrollSource}} in
        {{orientation}} and {{fill}} is <code>none</code> or <code>backwards</code>,

    :: return an unresolved time value.

    : Otherwise,

    :: return the [=effective time range=].

    </div>

    Note: Checking for {{endScrollOffset}} being the maximum scroll offset ensures that the common
    case of a 'whole scroller' ScrollTimeline does not become inactive when you scroll to the end.

1.  Return the result of evaluating the following expression:

    <blockquote>
      <code>(<var>current scroll offset</var> - {{startScrollOffset}}) / ({{endScrollOffset}} - {{startScrollOffset}}) &times; [=effective time range=]</code>
    </blockquote>

</div>  <!-- link-for-hint="ScrollTimeline" -->


## The 'animation-timeline' property ## {#animation-timeline}

A {{ScrollTimeline}} may be applied to a CSS Animation [[CSS3-ANIMATIONS]] using
the 'animation-timeline' property.

<pre class='propdef'>
Name: animation-timeline
Value: <<single-animation-timeline>>#
Initial: auto
Applies to: all elements, ::before and ::after pseudo-elements
Inherited: none
Animatable: no
Percentages: N/A
Media: interactive
Computed value: As specified
Canonical order: per grammar
</pre>

<dfn>&lt;single-animation-timeline></dfn> = auto | scroll([element(<<id-selector>>)[, <<scroll-direction>>[, <<scroll-offset>>[, <<scroll-offset>>[, <<time>>[, <<single-animation-fill-mode>>]]]]]])

<dfn>&lt;scroll-direction></dfn> = auto | block | inline | horizontal | vertical

<dfn>&lt;scroll-offset></dfn> = <<length>> | <<percentage>> | auto

The 'animation-timeline' property is similar to properties like 'animation-duration' and 
'animation-timing-function' in that it can have one or more values, each one imparting 
additional behavior to a corresponding [=animation=] on the 
element, with the timelines matched up with animations as described 
[[css-animations-1#animation-name|here]].

<div link-for-hint="ScrollTimeline">

Each value has type <<single-animation-timeline>>, whose possible values have the
following effects:

:   auto
::  The animation's [=timeline=] is a {{DocumentTimeline}}, more specifically the
    <a>default document timeline</a>.

:   scroll([element(<<id-selector>>)[, <<scroll-direction>>[, <<scroll-offset>>[, <<scroll-offset>>[, <<time>>[, <<single-animation-fill-mode>>]]]]]])
::  The animation's [=timeline=] is a {{ScrollTimeline}}.

    The timeline's {{scrollSource}} is the [=scroll container=] identified
    by the <<id-selector>>, defaulting to the element's nearest scrollable ancestor.

    The <<scroll-direction>>, if provided, determines the timeline's {{orientation}}.

    The first <<scroll-offset>>, if provided, determines the timeline's {{startScrollOffset}}.

    The second <<scroll-offset>>, if provided, determines the timeline's {{endScrollOffset}}.

    The <<time>> value, if specified, determines the timeline's {{timeRange}}.

    The <<single-animation-fill-mode>> value, if specified, determines the timeline's {{fill}}.

</div>  <!-- link-for-hint="ScrollTimeline" -->

## Examples ## {#timeline-examples}

<div class="example">
  Draw a reading progress bar along the top of the page as the user scrolls
  <pre class="lang-css">
    #progress {
      position: fixed;
      top: 0;
      width: 0;
      height: 2px;
      background-color: red;
    }
  </pre>
  <pre class="lang-javascript">
    if (window.matchMedia('(prefers-reduced-motion: no-preference)').matches) {
      let progress = document.getElementById("progress");
      let effect = new KeyframeEffect(
        progress,
        [
          { width: "0vw" },
          { width: "100vw" }
        ],
        {
          duration: 1000,
          easing: "linear"
        });
      let timeline = new ScrollTimeline({
        trigger: new ScrollTrigger({
          scrollSource: document.documentElement,
          orientation: "vertical",
          kind: "range"
        });
      });
      let animation = new Animation(effect, timeline);
      animation.play();
    }
  </pre>
</div>

<div class="example">
  The same thing with CSS, using 'animation-timeline'
  <pre class="lang-css">
    @media (prefers-reduced-motion: no-preference) {
      @keyframes progress {
        from {
          width: 0vw;
        }
        to {
          width: 100vw;
        }
      }
      #progress {
        position: fixed;
        top: 0;
        width: 0;
        height: 2px;
        background-color: red;
        animation-name: progress;
        animation-duration: 1s;
        animation-timing-function: linear;
        /* Assume the HTML element has id 'root' */
        animation-timeline: scroll(element(#root), vertical);
      }
    }
  </pre>
</div>

# Avoiding cycles with layout # {#avoiding-cycles}

The ability for scrolling to drive the progress of an animation, gives rise to 
the possibility of <dfn>layout cycles</dfn>, where a change to a scroll offset
causes an animation's effect to update, which in turn causes a new change to the
scroll offset.

To avoid such cycles, animations with a {{ScrollTimeline}} are sampled once
per frame, after scrolling in response to input events has taken place, but
before {{requestAnimationFrame()}} callbacks are run. If the sampling of such an
animation causes a change to a scroll offset, the animation will not be
re-sampled to reflect the new offset until the next frame.

The implication of this is that in some situations, in a given frame, the 
rendered scroll offset of a scroll container may not be consistent with the state 
of an animation driven by scrolling that scroll container. However, this will
only occur in situations where the animation's effect changes the scroll offset
of that same scroll container (in other words, in situations where the animation's
author is asking for trouble). In normal situations, including - importantly - 
when scrolling happens in response to input events, the rendered scroll offset
and the state of scroll-driven animations will be consistent in each frame.

User agents that composite frames asynchronously with respect to layout and/or
script may, at their discretion, sample scroll-driven animations once per 
<em>composited</em> frame, rather than (or in addition to) once per full layout 
cycle. Again, if sampling such an animation causes a change to a scroll offset, 
the animation will not be re-sampled to reflect the new offset until the next 
frame.

Nothing in this section is intended to require that scrolling block on layout
or script. If a user agent normally composites frames where scrolling has
occurred but the consequences of scrolling have not been fully propagated in
layout or script (for example, <code>scroll</code> event listeners have not yet 
run), the user agent may likewise choose not to sample scroll-driven animations
for that composited frame. In such cases, the rendered scroll offset and the
state of a scroll-driven animation may be inconsistent in the composited frame.

# Scroll-triggered (but time-driven) animations # {#scroll-triggered-animations}

An earlier draft of this proposal also provided for animations whose progress
was driven by time (as with existing animations), but whose activation was 
triggered by scrolling past a certain scroll offset or into a given scroll range.

The main objective was to allow triggering the animation from the compositor
thread. (The objective of scroll-linked animations is to make sure that 
the animation is in sync with the scroll position on each composited frame.
If the triggering doesn't happen on the compositor thread, then it's possible
that for a few frames the visual scroll position is such that the animation
should have started, but it has not in fact started yet because the main thread,
which is doing the triggering, is lagging behind.)

However, we found that in the vast majority of cases where a web author would
want to do this, they would want to do it for a CSS transition (as opposed to
a CSS animation). Unfortunately, it's not possible to trigger CSS transitions from 
the compositor thread (because triggering a transition requires style resolution, 
which cannot be performed on the compositor thread). Given the extent to which 
triggering complicated the API, we decided it wasn't worth it if you can't use 
it for transitions, so this feature was removed.

The design space for triggering animations is still open. We welcome input
on this subject.

<h2 id="appendix-a-considerations-for-security-and-privacy">Appendix A. Considerations for Security and Privacy</h2>

This appendix is <em>informative</em>.

There are no known security or privacy impacts of this feature.

The W3C TAG is developing a
<a href="https://www.w3.org/TR/security-privacy-questionnaire/">Self-Review Questionnaire: Security and Privacy</a>
for editors of specifications to informatively answer.

Per the <a href="https://www.w3.org/TR/security-privacy-questionnaire/#questions">Questions to Consider</a>

<ol>
<li>Does this specification deal with personally-identifiable information?
<p>No.</p>
</li>

<li>Does this specification deal with high-value data?
<p>No.</p>
</li>

<li>Does this specification introduce new state for an origin that persists across browsing sessions?
<p>No.</p>
</li>

<li>Does this specification expose persistent, cross-origin state to the web?
<p>No.</p>
</li>

<li>Does this specification expose any other data to an origin that it doesn’t currently have access to?
<p>No.</p>
</li>

<li>Does this specification enable new script execution/loading mechanisms?
<p>No.</p>
</li>

<li>Does this specification allow an origin access to a user’s location?
<p>No.</p>
</li>

<li>Does this specification allow an origin access to sensors on a user’s device?
<p>No.</p>
</li>

<li>Does this specification allow an origin access to aspects of a user’s local computing environment?
<p>No.</p>
</li>

<li>Does this specification allow an origin access to other devices?
<p>No.</p>
</li>

<li>Does this specification allow an origin some measure of control over a user agent’s native UI?
<p>No.</p>
</li>

<li>Does this specification expose temporary identifiers to the web?
<p>No.</p>
</li>

<li>Does this specification distinguish between behavior in first-party and third-party contexts?
<p>No.</p>
</li>

<li>How should this specification work in the context of a user agent’s "incognito" mode?
<p>No differently. The website should not be able to determine that the user is
in an "incognito" mode using scroll-linked animations.</p>
</li>

<li>Does this specification persist data to a user’s local device?
<p>No.</p>
</li>

<li>Does this specification have a "Security Considerations" and "Privacy Considerations" section?
<p>Yes.</p>
</li>

<li>Does this specification allow downgrading default security characteristics?
<p>No.</p>
</li>
</ol>

